//******************************************************************************
// Copyright (c) 2018 - 2019, The Regents of the University of California (Regents).
// All Rights Reserved. See LICENSE and LICENSE.SiFive for license details.
//------------------------------------------------------------------------------

package boom.util

import chisel3._
import chisel3.util._

/**
 * Implements the same interface as chisel3.util.Queue.
 * Effectively a two-entry Queue.
 *
 * @param gen the type of Data object to store in the register
 */
class ElasticReg[T <: Data](gen: T) extends Module
{
  val entries = 2
  val io = IO(new QueueIO(gen, entries) {})
  /*
  分情况理解：
  1. 有进，没有出
  2. 有出，没有进
  3. 进出同时发生
  */

  private val valid = RegInit(VecInit(Seq.fill(entries) { false.B }))
  private val elts  = Reg(Vec(entries, gen))

  for (i <- 0 until entries) 
  {
    def paddedValid(i: Int) = if (i == -1) 
                                      true.B 
                              else if (i == entries) //i==1
                                      false.B 
                              else                   //i==0
                                      valid(i)

    val wdata = if (i == entries-1)//i==1
                       io.enq.bits 
                else               //i==0
                    Mux(valid(i+1), elts(i+1), io.enq.bits)//Mux(valid(1), elts(1), io.enq.bits)

    val wen = Mux(io.deq.ready,
                  paddedValid(i+1) ||                io.enq.fire() && ((i == 0).B || valid(i)),
                  io.enq.fire() && paddedValid(i-1) && !valid(i))

    when (wen) { elts(i) := wdata }

    valid(i) := Mux(io.deq.ready,//有出请求
                    paddedValid(i+1) ||             io.enq.fire() && ((i == 0).B || valid(i)),
                    io.enq.fire() && paddedValid(i-1) || valid(i))
  }

  io.enq.ready := !valid(entries-1)// !valid(1) 是否满了
  io.deq.valid := valid(0)
  io.deq.bits  := elts.head//elts（0）

  io.count := PopCount(valid.asUInt)
}

/**
 * Companion object to ElasticReg which enqueues a data type
 * and returns a dequeued Data object.
 */
object ElasticReg
{
  def apply[T <: Data](enq: DecoupledIO[T]): DecoupledIO[T] = {
    val q = Module(new ElasticReg(enq.bits.cloneType))
    q.io.enq <> enq
    q.io.deq
  }
}
